package main

import (
	"config-client-v1/test/serial-server/pkg"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"github.com/jacobsa/go-serial/serial"
	"io"
	"time"
)

var Port io.ReadWriteCloser
var jsonFile *string
var list []*pkg.FrameItemParam

func main() {
	portName := flag.String("port_name", "COM7", "串口名")
	baudRate := flag.Int("baud_rate", 9600, "波特率")
	dataBits := flag.Int("data_bits", 8, "数据位")
	stopBits := flag.Int("stop_bits", 1, "停止位")
	jsonFile = flag.String("json_file", "rs485-1.json", "帧格式json文件")
	flag.Parse()
	err := loadingFrameItem()
	if err != nil {
		fmt.Printf("加载帧格式失败 %v\n", err.Error())
		return
	}

	options := serial.OpenOptions{
		PortName:        *portName,
		BaudRate:        uint(*baudRate),
		DataBits:        uint(*dataBits),
		StopBits:        uint(*stopBits),
		MinimumReadSize: 4,
		ParityMode:      serial.ParityMode(0),
	}
	port, err := serial.Open(options)
	if err != nil {
		fmt.Printf("串口打开失败 %s %v\n", *portName, err.Error())
		return
	}
	fmt.Printf("串口打开成功 %s\n", *portName)
	Port = port
	defer func() {
		_ = port.Close()
	}()
	go testSend()
	// 循环读取数据
	for {
		buffer := make([]byte, 4096) // 缓冲区大小
		n, err2 := port.Read(buffer)
		if err2 != nil {
			fmt.Printf("port.Read error %s\n", err2.Error())
			continue
		}
		// 输出读到的数据
		if n > 0 {
			time.Sleep(5 * time.Millisecond)
			recvData := pkg.ByteArrayToHexStr(buffer[:n])
			fmt.Printf("len=%d %v\n", n, recvData)
		}
	}
}

// COM9 端口模拟发送数据
func loadingFrameItem() (err error) {
	str, err := pkg.ReadFileContent(*jsonFile)
	if err != nil {
		err = errors.New(fmt.Sprintf("读取帧格式文件失败 %s\n", err))
		return
	}
	list = make([]*pkg.FrameItemParam, 0)
	err = json.Unmarshal([]byte(str), &list)
	if err != nil {
		return
	}
	if len(list) == 0 {
		err = errors.New("帧格式为空, 请重新编辑")
		return
	}
	return
}
func testSend() {
	for {
		data, err := createFrameRawData()
		if err != nil {
			fmt.Printf("生成数据帧失败 %s\n", err.Error())
			return
		}
		n, err := Port.Write(data)
		if err != nil {
			fmt.Printf("串口写入失败 %s\n", err.Error())
			continue
		}
		fmt.Printf("【%s】n=%d err=%v\n", pkg.GetNow(), n, err)
		time.Sleep(time.Second * 3)
	}
}
func createFrameRawData() (data []byte, err error) {
	data = make([]byte, 0)
	uniqueKeyBoolMap := make(map[string]bool)
	for i := 0; i < len(list); i++ {
		val := list[i]
		if list[i].Start > list[i].End {
			err = errors.New("开始字节不可大于结束字节")
			return
		}
		if i > 0 && len(list) > 1 {
			if !uniqueKeyBoolMap[fmt.Sprintf("%d", val.Start)] && list[i].Start != (list[i-1].End+1) {
				err = errors.New(fmt.Sprintf("[%s] 和前面字段不连续 %d~%d", val.FieldName, val.Start, val.End))
				return
			}
		}
		l := val.End - val.Start + 1
		if val.InputMethod == 0 {
			if val.RawData == "" {
				if val.Type != "校验" {
					err = errors.New("非变量且非校验的raw_data不可为空")
					return
				}
				if val.CheckStart > val.End {
					err = errors.New("开始字节不可大于结束字节")
					return
				}
				if val.CheckStart >= len(data) {
					err = errors.New("校验起始字节异常")
					return
				}
				if val.CheckEnd >= len(data) {
					err = errors.New("校验结束字节异常")
					return
				}
				switch val.CheckMethod {
				case "sum":
					if l != 1 {
						err = errors.New("sum校验和最多占用一个字节")
						return
					}
					byteSum := pkg.GetBytesSum(data, val.CheckStart, val.CheckEnd)
					data = append(data, byteSum)
				case "crc16":
					if l != 2 {
						err = errors.New("crc16校验最多占用两个字节")
						return
					}
					bytes := pkg.GetCrc16Result(data[val.CheckStart : val.CheckEnd+1])
					data = append(data, bytes...)
				default:
					err = errors.New("校验方法参数无效")
					return
				}
				continue
			} else {
				b, err2 := pkg.HexStrToByteArray(val.RawData)
				if err2 != nil {
					err = errors.New(fmt.Sprintf("%s  rawData值格式错误 %s", val.FieldName, err2.Error()))
					return
				}
				if len(b) != l {
					err = errors.New(fmt.Sprintf("%s  rawData值长度与占用字节不匹配", val.FieldName))
					return
				}
				data = append(data, b...)
			}
			continue
		}

		switch val.DataType {
		case "int":
			if l == 1 {
				val.SpecType = "uint8"
				data = append(data, uint8(pkg.GetRandAreaNumber(0, 1)))
			} else if l == 2 {
				// todo
				val.SpecType = "uint16"
				b := pkg.Uint16ToBytes(uint16(pkg.GetRandAreaNumber(10000, 65535)), "big")
				data = append(data, b[:]...)
			} else if l == 4 {
				val.SpecType = "uint32"
				b := pkg.Uint32ToBytes(uint32(pkg.GetRandAreaNumber(2000000, 3000000)), "big")
				data = append(data, b[:]...)
			} else {
				err = errors.New(fmt.Sprintf("%s 占用字节异常", val.FieldName))
				return
			}
		case "float":
			if l == 4 {
				val.SpecType = "float32"
				b := pkg.Float32ToByte4(float32(pkg.GetRandAreaNumber(8000, 10000))/float32(100.0), "big")
				data = append(data, b[:]...)
			} else {
				err = errors.New(fmt.Sprintf("%s 占用字节异常", val.FieldName))
				return
			}
		case "bool":
			val.SpecType = "uint8"
			if l != 1 {
				err = errors.New(fmt.Sprintf("%s 占用字节异常", val.FieldName))
				return
			}
			if val.MultiByte == 1 {
				// 多字节布尔值
				if val.BoolBits > 7 {
					err = errors.New(fmt.Sprintf("%s bool_bits 参数异常", val.FieldName))
					return
				}
				if !uniqueKeyBoolMap[fmt.Sprintf("%d", val.Start)] {
					data = append(data, uint8(pkg.GetRandAreaNumber(0, 255)))
					uniqueKeyBoolMap[fmt.Sprintf("%d", val.Start)] = true
				}
			} else {
				// 单字节布尔值
				data = append(data, uint8(pkg.GetRandAreaNumber(0, 1)))
			}
		default:
			err = errors.New(fmt.Sprintf("%s data_type 参数无效", val.FieldName))
			return
		}
	}
	return
}
